#pragma once
#include <iostream>

template<typename T>
inline void print_container(const std::vector<T>& cont)
{
	for (size_t i = 0; i < cont.size(); i++)
	{
		std::cout << cont[i] << " ";
	}
	std::cout << std::endl;
}

template<typename T>
inline void print_container(const std::set<T>& cont)
{
	auto itb = cont.begin(), itend = cont.end();

	for (; itb!= itend; itb++)
	{
		std::cout << *itb << " ";
	}
	std::cout << std::endl;
}

template<typename T> const char* separator = " " ;
template<> const char* separator<std::string> = "";

template<typename C>
inline void PrintAnyCont(const C& cont)
{
	for (const auto& x : cont)
	{
		std::cout << x << separator<C>;
	}
	std::cout << std::endl;
}

template<typename T> void negate(T& arg){ arg =-arg; };
template<> void negate<std::string>(std::string& str)
{
	for (auto& x : str)
	{
		if (x >= 'a' && x <= 'z') x -= 0x20;
		else if(x >= 'A' && x <= 'Z') x += 0x20;
	}	
}

std::string& operator-(std::string& cont)
{
	for (auto& k : cont)
	{
		if (islower(k))
			k = toupper(k);
		else
			k = tolower(k);
	}
	return cont;
}

template<typename C>
inline void NegateAll(C& cont)
{
	for (auto& x : cont)
	{
		//negate(x);
		x = -x;
	}//������������� ������� ����� ��� ������ ������ ���������� ��������
}

template<typename C>
inline void absSort( C& cont)
{
	// ��� ���������� ����� ������������ �������� sort(), � ��� ������� ������� - ������-�������
	std::sort(std::begin(cont), std::end(cont), [](const auto& x, const auto& y) {return abs(x) < abs(y); });
}

template<typename C1, typename C2>
inline auto SumCont(const C1& cont1, const C2& cont2)
{
	int c_size = (std::size(cont1) > std::size(cont2) ? std::size(cont1) : std::size(cont2));
	std::vector<decltype(*(std::begin(cont1)) + *(std::begin(cont2)))> v1(c_size);//���������� ��� ����� ��������� decltype 
	std::copy(std::begin(cont1), std::end(cont1), v1.begin());
	std::transform(std::begin(cont2), std::end(cont2), v1.begin(), v1.begin(), [](const auto& x, const auto& y) {return y + x; });
	return v1;
}

template<typename Cont_source, typename Cont1, typename Cont2, typename lambda1>
inline void Separate(const Cont_source source, Cont1& first, Cont2& second, lambda1 L)
{//������ ��������� �������� ���������� �� ������ � ��� ��� ���������??
	for (const auto x : source)
	{
		//L(x) ? *std::back_inserter(first) = x : *std::back_inserter(second) = x;
		if (L(x))
		{
			*std::back_inserter(second) = x;
		}
		else
		{
			*std::back_inserter(first) = x;
		}
	}
}
enum class COLORS :char { RED = 2, BLUE = 4, GREEN = 8 };
enum class months :unsigned char { January, February, March/*...*/ };
enum class weekDays { Monday, Tuesday /*...*/ };

template<typename T> std::map<std::string,T> Map;

template<> inline std::map<std::string, COLORS> Map<COLORS>
{ {"Red", COLORS::RED}, { "Blue",COLORS::BLUE }, {"Green", COLORS::GREEN}};
template<> inline std::map<std::string, months> Map<months>
{ {"January", months::January}, { "February", months::February }, { "March", months::March }};
template<>inline std::map<std::string, weekDays> Map<weekDays>
{};


template<typename T>
inline std::string enumToString(T enum_value)
{	
	//������� �� �������� ���� �����������&,
	//����� � ������� ����������
	for (const auto& [x,y]: Map<T>)
	{
		if (enum_value == y) return x;
	}
	throw std::out_of_range("does not exist in the enum");
}

template<typename T>
inline T stringToEnum(const std::string& str)
{
	//if (Map<T>.find(str) != Map<T>.end())
	//{
	//	return Map<T>[str];
	//}
	//throw out_of_range("does not exist in the enum");//��� at()

	return Map<T>.at(str);
}